﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace Kenly.DBFramework.Gateway
{
    /// <summary>
    /// Factory to create GatewayConfig for all possible types of a assembly.
    /// </summary>
    public abstract class ConfigFactory : IConfigFactory
    {
        /// <summary>
        /// Gets a assembly from which factory creates all possible configs.
        /// </summary>
        public Assembly Assembly
        {
            get;
            private set;
        }

        /// <summary>
        /// 
        /// </summary>
        public IDictionary<Type, GatewayConfig> Configs
        {
            get;
            private set;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="assembly"></param>
        protected ConfigFactory(Assembly assembly)
        {
            CreateFactory(assembly);
        }

        private void CreateFactory(Assembly assembly)
        {
            if (assembly == null)
            {
                throw new ArgumentNullException("assembly");
            }

            this.Assembly = assembly;
            CreateConfigs(assembly);
        }

        private void CreateConfigs(Assembly assembly)
        {
            GatewayConfig defaultConfig = this.CreateDefaultConfig();
            this.Configs = ConfigFactory.CreateConfigs(assembly, defaultConfig);
            this.CustomizeConfigs();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static IList<Type> FindGatewayTypes(Assembly assembly)
        {
            List<Type> gatewayTypes = new List<Type>();
            if (assembly == null)
            {
                return gatewayTypes;
            }

            Type[] allTypes = assembly.GetTypes();
            if (allTypes == null || allTypes.Length == 0)
            {
                return gatewayTypes;
            }

            foreach (var item in allTypes)
            {
                if (IsGatewayType(item))
                {
                    gatewayTypes.Add(item);
                }
            }

            return gatewayTypes;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static bool IsGatewayType(Type type)
        {
            if (type == null)
            {
                return false;
            }

            Type viewGatewayType = typeof(IViewGateway);
            Type tableGatewayType = typeof(ITableGateway);
            Type storedProcedureType = typeof(IStoredProcedure);
            Type aggregateEntityType = typeof(IAggregateEntity);

            if (viewGatewayType.IsAssignableFrom(type) ||
                tableGatewayType.IsAssignableFrom(type) ||
                storedProcedureType.IsAssignableFrom(type) ||
                aggregateEntityType.IsAssignableFrom(type))
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="assembly"></param>
        /// <param name="defaultConfig"></param>
        /// <returns></returns>
        public static IDictionary<Type, GatewayConfig> CreateConfigs(Assembly assembly, GatewayConfig defaultConfig)
        {
            IDictionary<Type, GatewayConfig> configs = new Dictionary<Type, GatewayConfig>();
            if (assembly == null ||
                defaultConfig == null || !defaultConfig.IsValid)
            {
                return configs;
            }

            IList<Type> gatewayTypes = ConfigFactory.FindGatewayTypes(assembly);
            if (gatewayTypes.Count == 0)
            {
                return configs;
            }

            foreach (var gatewayType in gatewayTypes)
            {
                configs.Add(gatewayType, CopyDefaultConfig(defaultConfig));
            }

            return configs;
        }

        private static GatewayConfig CopyDefaultConfig(GatewayConfig defaultConfig)
        {
            return new GatewayConfig
            {
                ConnectionString = defaultConfig.ConnectionString,
                DatabaseType = defaultConfig.DatabaseType,
                CommandTimeout = defaultConfig.CommandTimeout
            };
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected abstract GatewayConfig CreateDefaultConfig();

        /// <summary>
        /// 
        /// </summary>
        protected abstract void CustomizeConfigs();
    }
}
